home *** CD-ROM | disk | FTP | other *** search
/ Complete Linux / Complete Linux.iso / docs / apps / database / ingres04.lzh / source / dbu / copy.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-01-18  |  20.1 KB  |  969 lines

  1. # include    <stdio.h>
  2. # include    <func.h>
  3. # include    <pv.h>
  4. # include    <ingres.h>
  5. # include    <aux.h>
  6. # include    <access.h>
  7. # include    <symbol.h>
  8. # include    <lock.h>
  9. # include    <signal.h>
  10. # include    <errors.h>
  11.  
  12.  
  13. /*
  14. **  COPY -- Performs an ingres COPY.
  15. **
  16. **    Trace Flags:
  17. **        30
  18. */
  19.  
  20.  
  21. # define    MAXMAP        3 * MAXDOM
  22. # define    DUMMY        'd'
  23. # define    ESCAPE        '\\'
  24.  
  25. extern short    tTdbu[100];
  26. extern int    copy();
  27. extern int    null_fn();
  28. extern char     *dumvalue();
  29.  
  30. struct fn_def CopyFn =
  31. {
  32.     "COPY",
  33.     copy,
  34.     null_fn,
  35.     null_fn,
  36.     NULL,
  37.     0,
  38.     tTdbu,
  39.     100,
  40.     'Z',
  41.     0
  42. };
  43.  
  44.  
  45.  
  46.  
  47. struct map
  48. {
  49.     char    name[MAXNAME+1];    /* attribute name */
  50.     char    ftype;        /* attfrmt of file domain */
  51.     char    rtype;        /* attfrmt of relation domain */
  52.     int    flen;        /* attfrml of file domain */
  53.     int    rlen;        /* attfrml of relation domain */
  54.     int    roffset;    /* attoff of relation domain */
  55.     int    used;        /* tag for duplicate checking */
  56.     char    *fdelim;    /* pointer to list of file param delims */
  57.     char    *paramname;    /* pointer to original parameter name */
  58.                 /* used for supplying domain name in case of error */
  59. };
  60. struct map    Map[MAXMAP];        /* one entry for each user
  61.                    specified domain in copy statement. */
  62.  
  63. int    Mapcount;        /* number of Map entries   */
  64.  
  65.  
  66. DESC    Des;        /* descriptor for copied relation     */
  67.  
  68. extern struct out_arg    Out_arg;    /* user defined formats for numeric output */
  69.  
  70. FILE    *File_iop;        /* i/o file pointer */
  71. char    *Filename;        /* pointer to file name */
  72.  
  73. int    Into;            /* into is one if this is a copy into file */
  74.  
  75. char    Inbuf[BUFSIZ];        /* input holder */
  76. char    Outbuf[BUFSIZ];        /* output holder */
  77.  
  78. long    Tupcount;        /* number of tuples processed */
  79. char    *Relname;        /* name of relation */
  80. long    Duptuple;        /* number of duplicate tuples */
  81. long    Baddoms;        /* number of domains with control chars */
  82. long    Truncount;        /* number of truncations on a c0 field */
  83. int    Piped[2];        /* pipe descriptor for copy communication */
  84. char    *Cpdomains[] =        /* dummy domain names for copy "into" */
  85. {
  86.     "nl",        "\n",
  87.     "tab",        "\t",
  88.     "sp",        " ",
  89.     "nul",        "\0",
  90.     "null",        "\0",
  91.     "comma",    ",",
  92.     "colon",    ":",
  93.     "dash",        "-",
  94.     "lparen",    "(",
  95.     "rparen",    ")",
  96.     0
  97. };
  98.  
  99. char    Delimitor[] =    ",\n\t";    /* default delims for c0 & d0 */
  100.  
  101.  
  102.  
  103. copy(pc,pv)
  104. int    pc;
  105. PARM    pv[];
  106. {
  107.     extern char    *Usercode;
  108.     extern int    Noupdt;
  109.     register int    i, pid;
  110.     register char    *cp;
  111.     int        stat;
  112.     void        copydone();
  113.     int        op;
  114.  
  115. #    ifdef xZTR1
  116.     if (tTf(30,1))
  117.     {
  118.         printf("entered copy\n");
  119.         prvect(pc, pv);
  120.     }
  121. #    endif
  122.     Duptuple = 0;
  123.     Truncount = 0;
  124.     Tupcount = 0;
  125.     Baddoms = 0;
  126.     Relname = pv[0].pv_val.pv_str;
  127.     Into = (pv[pc-2].pv_val.pv_str[0] == 'i');
  128.     Filename = pv[pc-1].pv_val.pv_str;
  129.  
  130.     /* relation must exist and not be a system relation */
  131.     /* in addition a copy "from" can't be done if the user */
  132.     /* doesn't own the relation */
  133.     /* and furthermore it can't have an index */
  134.     i = 0;    /* assume all is well */
  135.     if (op = openr(&Des, OR_WRITE, Relname))
  136.     {
  137.         if (op == AMOPNVIEW_ERR)
  138.             i = NOCPVIEW;
  139.         else
  140.         {
  141.             if (op < 0)
  142.                 syserr("COPY: openr 1 (%.14s) %d", Relname, op);
  143.             else
  144.                 /* non-existant relation */
  145.                 i = NOEXIST;
  146.         }
  147.     }
  148.     else
  149.     {
  150.         if (Into)
  151.         {
  152.             if ((Des.reldum.relstat & S_PROTALL)
  153.                 && (Des.reldum.relstat & S_PROTRET)
  154.                 && !bequal(Usercode, Des.reldum.relowner, UCODE_SZ))
  155.                 i = RELPROTECT;
  156.         }
  157.         else
  158.         {
  159.             /* extra checking if this is a copy "from" */
  160.  
  161.             /* must be owned by the user */
  162.             if (!bequal(Usercode, Des.reldum.relowner, UCODE_SZ))
  163.                 i = NOTOWNER;
  164.             else
  165.                 /* must be updateable */
  166.                 if ((Des.reldum.relstat & S_NOUPDT) && Noupdt)
  167.                     i = NOUPDT;
  168.                 else
  169.                     /* must not be indexed */
  170.                     if (Des.reldum.relindxd > 0)
  171.                         i = DESTINDEX;
  172.         }
  173.     }
  174.     if (i)
  175.     {
  176.         closer(&Des);
  177.         return (error(i, Relname, 0));    /* relation doesn't exist for this user */
  178.     }
  179.  
  180.     /* check that file name begins with a "/" */
  181.     cp = Filename;
  182.     while (*cp == ' ')
  183.         cp++;
  184.     if (*cp != '/')
  185.     {
  186.         closer(&Des);
  187.         return (error(FULLPATH, Filename, 0));
  188.     }
  189.  
  190.     /* fill map structures with transfer information */
  191.     if (i = mapfill(&pv[1]))
  192.     {
  193.         closer(&Des);
  194.         return (i);    /* error in user semantics */
  195.     }
  196.  
  197.     /* fork a child process which will run as the real user */
  198.     /* that child will complete the copy and exit */
  199.     if (pipe(Piped))
  200.         syserr("copy:can't make pipe");
  201.     if ((pid = fork()) < 0)
  202.         syserr("copy:can't fork");
  203.     if (pid)
  204.     {
  205.         /* the ingres parent */
  206.         close(Piped[1]);
  207.         ruboff(0);    /* interrupts off */
  208.         stat = fullwait(pid, "copy");
  209.         if (read(Piped[0], &Des.reladds, 4) != 4)
  210.             syserr("copy:can't read pipe");
  211.         close(Piped[0]);
  212.         closer(&Des);    /* close the rel */
  213.         rubon();
  214.         /* if stat is != 0 then add on 5800 for error */
  215.         if (stat)
  216.             stat += 5800;
  217.         return (stat);    /* done */
  218.     }
  219.  
  220.     /* the child. change to run as the real user */
  221.     if (signal(SIGINT, SIG_IGN) != SIG_IGN)
  222.         signal(SIGINT, copydone);    /* clean up on rubout */
  223.     setuid(getuid());
  224.     setgid(getgid());
  225.     if (Into)    /* from relation into file */
  226.     {
  227.         if ((File_iop = fopen(Filename, "w")) == NULL) /* create file for user */
  228.             i = nferror(NOFILECRT, Filename, 0);    /* cant create file */
  229.         else
  230.         {
  231.             if (Lockrel)    /* set a shared lock on relation*/
  232.                 setrll(A_SLP, Des.reltid.ltid, M_SHARE);
  233.             i = rel_file();
  234.         }
  235.     }
  236.     else        /* from UNIX file into relation */
  237.     {
  238.         if ((File_iop = fopen(Filename, "r")) == NULL)
  239.             i = nferror(NOFILEOPN, Filename, 0);    /* cant open user file */
  240.         else
  241.         {
  242.             if (Lockrel)    /* set an exclusive lock on relat*/
  243.                 setrll(A_SLP, Des.reltid.ltid, M_EXCL);
  244.             i = file_rel();
  245.             if (Duptuple)
  246.                 nferror(DUPTUPS, locv(Duptuple), 0);    /* warning only */
  247.             if (Baddoms)
  248.                 nferror(BADDOMS, locv(Baddoms), 0);    /* warning only */
  249.         }
  250.     }
  251.     copydone(i);
  252. }
  253. /*
  254. **    Finish up and exit after a copy or interrupt
  255. **
  256. **    I is the return code. Since only a byte can be
  257. **    returned, only the least significant 2 decimal
  258. **    digits are returned. i is either 0 or a number like 58??
  259. */
  260.  
  261. copydone(i)
  262. int    i;
  263. {
  264.     if (Lockrel)    /* unlock relation */
  265.         unlrl(Des.reltid.ltid);
  266.     if (Truncount)
  267.         nferror(TRUNCCHARS, locv(Truncount), 0);    /* warning only */
  268.     /*  force the updates to be flushed */
  269.     cleanrel(&Des);
  270.     if (File_iop)
  271.         fclose(File_iop);
  272.     if (write(Piped[1], &Des.reladds, 4) != 4)
  273.         syserr("copyc:can't writepipe");
  274.     exit (i % 100);
  275. }
  276. /*
  277. **  REL_FILE -- copy from relation to file
  278. */
  279.  
  280. rel_file()
  281. {
  282.     int            j;
  283.     struct tup_id        tid, limtid;
  284.     char            *cp, save;
  285.     register int        offset;
  286.     register int        i;
  287.     register struct map    *mp;
  288.  
  289.     /* set scan limits to scan the entire relation */
  290.     if (find(&Des, NOKEY, &tid, &limtid))
  291.         syserr("find error");
  292.  
  293.     while ((i = get(&Des, &tid, &limtid, Inbuf, 1)) == 0)
  294.     {
  295.         mp = Map;
  296.         offset = 0;
  297.         for (i = 0; i < Mapcount; i++)
  298.         {
  299.             /*
  300.             ** For cases of char to numeric conversion,
  301.             ** there must be a null byte at the end of the
  302.             ** string. The character just past the current
  303.             ** domain is saved an a null byte inserted 
  304.             */
  305.  
  306.             cp = &Inbuf[mp->roffset + mp->rlen];    /* compute address */
  307.             save = *cp;    /* get the character */
  308.             *cp = '\0';    /* insert a null */
  309.  
  310.             /*
  311.             ** Special case, we want to copy the tid
  312.             */
  313.             if ( mp->roffset == -1 )
  314.                 j = transfer(&tid,mp->rtype,mp->rlen,
  315.                          mp->ftype,mp->flen,offset);
  316.             else
  317.                 j = transfer(&Inbuf[mp->roffset], mp->rtype,
  318.                      mp->rlen, mp->ftype, mp->flen, offset);
  319.             if (j)
  320.             {
  321.                 /* bad ascii to numeric conversion or field length too small */
  322.                 return (nferror(j, mp->paramname, &Inbuf[mp->roffset], locv(Tupcount), Relname, Filename, 0));
  323.             }
  324.             *cp = save;    /* restore the saved character */
  325.             offset += mp->flen;
  326.             mp++;
  327.         }
  328.         Tupcount++;
  329.         if (fwrite(Outbuf, 1, offset, File_iop) != offset)
  330.             syserr("copy:cant write to user file %s", Filename);
  331.     }
  332.     if (i < 0)
  333.         syserr("bad get from rel %d", i);
  334.     return (0);
  335. }
  336. /*
  337. **    file_rel is called to transfer tuples from
  338. **    the input file and append them to the relation
  339. **
  340. **    Char domains are initialized to blank and numeric
  341. **    domains are initialized to zero.
  342. */
  343.  
  344. file_rel()
  345. {
  346.     register int        i, j;
  347.     register struct map    *mp;
  348.     struct tup_id        tid;
  349.  
  350.     clr_tuple(&Des, Outbuf);
  351.  
  352.     /* copy domains until an end of file or an error */
  353.     for (;;)
  354.     {
  355.         mp = Map;
  356.         for (i = 0; i < Mapcount; i++)
  357.         {
  358.             if ((j = bread(mp)) <= 0)
  359.             {
  360.                 if (j < 0)
  361.                 {
  362.                     i = 1;    /* force an error */
  363.                     j = UNDETC0;    /* unterminated string */
  364.                 }
  365.                 else
  366.                     j = UNEXEOF;    /* end of file */
  367.                 if (i)    /* error only if end of file during a tuple or unterminated string */
  368.                 {
  369.                     i = nferror(j, mp->paramname, locv(Tupcount), Filename, Relname, 0);
  370.                 }
  371.                 return (i);
  372.             }
  373.             j = transfer(Inbuf, mp->ftype, mp->flen, mp->rtype, mp->rlen, mp->roffset);
  374.             if (j)
  375.             {
  376.                 /* bad ascii to numeric or field length too small */
  377.                 return (nferror(j, mp->paramname, Inbuf, locv(Tupcount), Filename, Relname, 0));
  378.             }
  379.             mp++;
  380.         }
  381.         Tupcount++;
  382.         if ((j = insert(&Des, &tid, Outbuf, 1)) < 0)
  383.             syserr("insert error %d rel=%s", j, Relname);
  384.         if (j == 1)
  385.             Duptuple++;
  386.         mp++;
  387.     }
  388.     /* 
  389.     ** This statement was here -- i don'T think it does anything, but we'll see
  390.     ** return (0); 
  391.     */
  392. }
  393. /*
  394. **    transfer copies data from "*in" to
  395. **    Outbuf doing conversions whenever
  396. **    necessary
  397. */
  398.  
  399. transfer(in, sf, sl, df, dl, doff)
  400. ANYTYPE    *in;    /* pointer to input chars */
  401. char    sf;    /* source format */
  402. int    sl;    /* source length */
  403. char    df;    /* destination format */
  404. int    dl;    /* destination length */
  405. int    doff;    /* destination offset */
  406. {
  407.     register char        *outp;
  408.     register ANYTYPE    *inp;
  409.     register int        i;
  410.     int            j;
  411.     short            smalli;
  412.     char            temp[MAXFIELD];    /* holds char during conversions to ascii */
  413.     float            f;
  414.     double            d;
  415.     long            l;
  416.  
  417.  
  418.     outp = &Outbuf[doff];
  419.     inp = in;
  420.  
  421.     if (sf == DUMMY)
  422.         /* if source format is a dummy fields then
  423.            nothing else need be done */
  424.         return (0);
  425.  
  426.     if (df == DUMMY)
  427.     {
  428.         /* fill field with dummy domain character */
  429.         i = dl;    /* i equals the number of chars */
  430.         while (i--)
  431.             *outp++ = sf;    /* sf holds dummy char */
  432.         return (0);
  433.     }
  434.  
  435.     if (sf != CHAR)
  436.     {
  437.         if (df == CHAR)    /* numeric to char conversion */
  438.         {
  439.             switch (sl)
  440.             {
  441.               /* int of size 1 or 2 */
  442.               case 1:
  443.                 itoa(inp->i1type, temp);
  444.                 break;
  445.  
  446.               case 2:
  447.                 itoa(inp->i2type, temp);    /* convert to ascii */
  448.                 break;
  449.  
  450.               /* int or float of size 4 */
  451.               case 4:
  452.                 if (sf == INT)
  453.                 {
  454.                     smove(locv(inp->i4type), temp);    /* convert and copy */
  455.                 }
  456.  
  457.                 else
  458.                 {
  459.                     ftoa(inp->f4type, temp, dl, Out_arg.f4prec, Out_arg.f4style);
  460.                 }
  461.                 break;
  462.  
  463.               /* float of size 8 */
  464.               case 8:
  465.                 ftoa(inp->f8type, temp, dl, Out_arg.f8prec, Out_arg.f8style);
  466.                 break;
  467.  
  468.               /* there is no possible default */
  469.               default:
  470.                 syserr("bad domain length %d",sl);
  471.             }
  472.  
  473.             j = length(temp);
  474.             if ((i = dl - j) < 0)
  475.                 return (5808);    /* field won't fit */
  476.  
  477.             /* blank pad from left. Number will be right justified */
  478.             while (i--)
  479.                 *outp++ = ' ';
  480.  
  481.             bmove(temp, outp, j);
  482.             return (0);
  483.         }
  484.  
  485.         if (convert(inp, outp, sf, sl, df, dl))    /* numeric to numeric transfer */
  486.             return (DOMTOOSMALL);    /* numeric truncation error */
  487.         return (0);
  488.     }
  489.  
  490.     /* character to numeric conversion */
  491.     /* and character to character conversion */
  492.     switch (df)
  493.     {
  494.  
  495.       case CHAR:
  496.         i = sl;
  497.         if (!i)
  498.         {
  499.             i = length(inp->c0type);
  500.         }
  501.         if (i > dl)
  502.             i = dl;
  503.         if (charmove(inp->c0type, outp, i))
  504.             Baddoms++;
  505.         for (outp += i; i<dl; i++)
  506.             *outp++ = ' ';
  507.         return (0);
  508.  
  509.       case FLOAT:
  510.         if (atof(inp->c0type, &d))
  511.             return (BADINPUT);    /* bad conversion to numeric */
  512.         if (dl == 8)
  513.             bmove(&d, outp, dl);
  514.         else
  515.         {
  516.             f = d;    /* f8 to f4 conversion */
  517.             bmove(&f, outp, dl);
  518.         }
  519.         return (0);
  520.  
  521.       case INT:
  522.         if (dl == 4)
  523.         {
  524.             if (atol(inp->c0type, &l))
  525.                 return (5809);
  526.             bmove(&l, outp, 4);
  527.             return (0);
  528.         }
  529.         smalli = atoi(inp->c0type);
  530.                 if (dl == 1) {
  531.                         if ((smalli < -128) || (smalli > 127))
  532.                                 return (5809);
  533.                         df = smalli;
  534.                         bmove(&df, outp, dl);
  535.                 } else
  536.                         bmove(&smalli, outp, dl);
  537.         return (0);
  538.     }
  539. }
  540. /*
  541. **    moves a character string from "in"
  542. **    to "out" removing any control characters.
  543. **    returns true if any control characters were found
  544. */
  545.  
  546. charmove(in, out, length)
  547. char    *in, *out;
  548. int    length;
  549. {
  550.     register char    *ip, *op;
  551.     register int    l;
  552.     int        bad;
  553.  
  554.     bad = FALSE;
  555.     ip = in;
  556.     op = out;
  557.     l = length;
  558.  
  559.     while (l--)
  560.         if ((*op++ = *ip++) < ' ')
  561.         {
  562.             *(op-1) = ' ';
  563.             bad = TRUE;
  564.         }
  565.     return (bad);
  566. }
  567. /*
  568. **    Mapfill fills the Map structure with the list
  569. **    of user supplied attributes. It then reads
  570. **    the list of relation attributes and checks
  571. **    for matching attribute names.
  572. **
  573. **    if an error occures then mapfill returns -1
  574. **        else it returns 0
  575. **
  576. **    Mapfill performs special processing on
  577. **    dummy domains.
  578. **
  579. **    If no user attributes are given, then "given"=FALSE
  580. **    and each attribute in the relation is set up to be
  581. **    copied in the formats and order in which they
  582. **    exist in the relation
  583. */
  584.  
  585. mapfill(aptr)
  586. PARM    aptr[];
  587. {
  588.     register PARM        *ap;
  589.     register struct map    *mp;
  590.     register int        i;
  591.     char            *fp;
  592.     extern DESC        Attdes;
  593.     struct attribute    att;
  594.     struct tup_id        tid, limtid;
  595.     int            given, cnt;
  596.     char            *zcheck();
  597.  
  598.     Mapcount = 0;
  599.     mp = Map;
  600.     ap = aptr;
  601.  
  602.     /* Gather list of user supplied attributes */
  603.  
  604.     while (*(ap->pv_val.pv_str) != '\0')
  605.     {
  606.         /* check for overflow */
  607.         if (Mapcount == MAXMAP)
  608.             return (error(TOOMANYATTR, 0));    /* more than MAXMAP specifiers */
  609.  
  610.         mp->paramname = (ap->pv_val).pv_str;    /* save pointer to user supplied name */
  611.         pmove(((ap++)->pv_val).pv_str, mp->name, MAXNAME, ' ');
  612.         fp = ((ap++)->pv_val).pv_str;    /* fp points to format string */
  613.         mp->used = 0;
  614.         mp->rlen = 0;    /* zero in case this is a dummy domain */
  615.         mp->roffset = 0;
  616.         mp->fdelim = 0;
  617.         /* check domain type in *fp */
  618.         switch (*fp++)
  619.         {
  620.  
  621.           case 'c':
  622.             i =  CHAR;
  623.             if ((mp->fdelim = zcheck(fp)) == 0)
  624.                 return (-1);    /* bad delimitor */
  625.             break;
  626.  
  627.           case 'f':
  628.             i = FLOAT;
  629.             break;
  630.  
  631.           case 'i':
  632.             i = INT;
  633.             break;
  634.  
  635.           case DUMMY:
  636.             i = DUMMY;
  637.             if ((mp->fdelim = zcheck(fp)) == 0)
  638.                 return (-1);
  639.             break;
  640.  
  641.           default:
  642.             return (error(BADATTRTYPE, mp->paramname, --fp, 0));
  643.         }
  644.         mp->ftype = i;
  645.  
  646.  
  647.         /* convert format length to binary */
  648.         mp->flen = atoi(fp);
  649.         if (mp->flen < 0 ||
  650.             mp->flen > 511 ||
  651.             (mp->ftype == FLOAT && mp->flen != 4 && mp->flen != 8) ||
  652.             (mp->ftype == INT && mp->flen != 1 && mp->flen != 2 && mp->flen != 4))
  653.         {
  654.             return (error(BADATTRLEN, mp->paramname, --fp, 0));    /* bad length for attribute */
  655.         }
  656.  
  657.         /* process dummy domain if any */
  658.         if (Into && mp->ftype == DUMMY && mp->flen)
  659.         {
  660.             if ((fp = dumvalue(mp->paramname)) == 0)
  661.                 return (5807);    /* bad dummy name */
  662.             mp->rtype = *fp;    /* use first char of string */
  663.         }
  664.  
  665.         /* check for format of type "c0delim" on copy "into" */
  666.         if (Into && mp->flen == 0 && mp->fdelim != Delimitor)
  667.         {
  668.             fp = mp->fdelim;
  669.  
  670.             /* is there room for a dummy domain? */
  671.             mp++;
  672.             if (++Mapcount == MAXMAP)
  673.                 return (error(TOOMANYATTR, 0));    /* no room */
  674.  
  675.             /* create a dummy entry */
  676.             mp->ftype = DUMMY;
  677.             mp->flen = 1;
  678.             mp->rtype = *fp;
  679.             mp->roffset = mp->rlen = 0;
  680.         }
  681.  
  682.         mp++;
  683.         Mapcount++;
  684.     }
  685.     /* if no atributes were given, set flag */
  686.     if (Mapcount)
  687.         given = TRUE;
  688.     else
  689.         given = FALSE;
  690.  
  691.     /* open attribute relation and prepare for scan */
  692.     opencatalog("attribute", OR_READ);
  693.  
  694.     setkey(&Attdes, &att, Des.reldum.relid, ATTRELID);
  695.     setkey(&Attdes, &att, Des.reldum.relowner, ATTOWNER);
  696.  
  697.     if (find(&Attdes, EXACTKEY, &tid, &limtid, &att))
  698.         syserr("find error for att-rel");
  699.  
  700.     /* scan Map for each relation attribute */
  701.     while ((i = get(&Attdes, &tid, &limtid, &att, 1)) == 0)
  702.     {
  703.         if (!bequal(&Des, &att, MAXNAME+2))
  704.             continue;
  705.         /* if no user attributes were supplied, fake an entry */
  706.         if (!given)
  707.         {
  708.             Mapcount++;
  709.             mp = &Map[att.attid -1];
  710.             mp->rtype = mp->ftype = att.attfrmt;
  711.             mp->rlen = mp->flen = att.attfrml & I1MASK;
  712.             mp->roffset = att.attoff;
  713.             mp->used = 1;
  714.             mp->paramname = mp->name;    /* point to name */
  715.             bmove(att.attname, mp->name, MAXNAME);    /* copy name */
  716.             continue;
  717.         }
  718.         mp = Map;
  719.  
  720.         /* check each user domain for match with relation domain */
  721.         for (i = Mapcount; i--;  mp++)
  722.         {
  723.             if (mp->ftype == DUMMY)
  724.                 continue; /* ignore dummy */
  725.             if (!bequal(mp->name, att.attname, 12))
  726.                 continue;
  727.  
  728.             mp->rtype = att.attfrmt;
  729.             mp->rlen = att.attfrml & I1MASK;
  730.             mp->roffset = att.attoff;
  731.             mp->used++;
  732.  
  733.             /* check for special case of C0 in a copy "into" */
  734.             if (Into && (mp->flen == 0) && mp->ftype == CHAR)
  735.             {
  736.                 switch (mp->rtype)
  737.                 {
  738.                   case CHAR:
  739.                     mp->flen = mp->rlen;
  740.                     break;
  741.     
  742.                   case INT:
  743.                     switch (mp->rlen)
  744.                     {
  745.  
  746.                       case 1:
  747.                         mp->flen = Out_arg.i1width;
  748.                         break;
  749.  
  750.                       case 2:
  751.                         mp->flen = Out_arg.i2width;
  752.                         break;
  753.  
  754.                       case 4:
  755.                         mp->flen = Out_arg.i4width;
  756.                     }
  757.                     break;
  758.     
  759.                   case FLOAT:
  760.                     if (mp->rlen == 4)
  761.                         mp->flen = Out_arg.f4width;
  762.                     else
  763.                         mp->flen = Out_arg.f8width;
  764.                 }
  765.             }
  766.             /*  if this is a copy "from" then break
  767.                 otherwise continue. In a copy "into"
  768.                 an attribute might be copied more than once */
  769.             if (!Into)
  770.                 break;
  771.         }
  772.     }
  773.     if (i < 0)
  774.         syserr("bad get from att-rel %d", i);
  775.  
  776.     /* check that all user domains have been identified */
  777.     cnt = 0;
  778.     mp = Map;
  779.     for (i = Mapcount; i--; mp++)
  780.     {
  781.         cnt += mp->flen;
  782.         if (mp->ftype == DUMMY)
  783.             continue;
  784.         if (!mp->used)
  785.         {
  786.             if ( Into && bequal(mp->name,"tid           ",12) )
  787.             {
  788.                 mp->flen = 4;
  789.                 mp->rtype = INT;
  790.                 mp->rlen = 4;
  791.                 mp->roffset = -1;
  792.                 mp->used++;
  793.             }
  794.             else
  795.                 return (error(ATTRNOEXIST, mp->paramname, Relname, 0));    /* unrecognizable domain name */
  796.         }
  797.     }
  798.     /* check that copy into doesn't exceed buffer size */
  799.     if (Into && cnt > BUFSIZ)
  800.         return (error(FILETOOWIDE, 0));    /* cnt too large */
  801.     return (0);
  802. }
  803. /*
  804. **  BREAD
  805. */
  806.  
  807. bread(mp)
  808. struct map    *mp;
  809. {
  810.     register int    count,    i;
  811.     register char    *inp;
  812.     char        *dl;
  813.     int        esc;    /* escape flag */
  814.  
  815.     count = mp->flen;
  816.     inp = Inbuf;
  817.  
  818.     if (count)
  819.     {
  820.         /* block mode. read characters */
  821.         i = fread(inp, 1, count, File_iop);
  822.  
  823.         /* null terminate */
  824.         *(inp + count) = '\0';
  825.  
  826.         return (i == count);    /* true -> normal, false ->eof */
  827.     }
  828.  
  829.     /* string mode read */
  830.     /*
  831.     ** Determine the maximum size the C0 field being read can be.
  832.     ** In the case where it is being copied into a CHAR field, then
  833.     ** the size is that of the char field (+1 for the delimitor).
  834.     ** In the case of a numeric, it is limited only by the size of the
  835.     ** buffer area.
  836.     */
  837.     count = mp->rtype == CHAR ? mp->rlen + 1 : BUFSIZ;
  838.     esc = FALSE;
  839.  
  840.     for (;;)
  841.     {
  842.         if ((i = getc(File_iop)) == EOF)
  843.             return (inp == Inbuf ? 0 : -1);    /* -1 -> unexpected EOF, 0 -> normal EOF */
  844.  
  845.         if (count > 0)
  846.         {
  847.             count--;
  848.             *inp++ = i;
  849.         }
  850.         else
  851.         {
  852.             if (count == 0)
  853.             {
  854.                 /* determine type of overflow */
  855.                 if (mp->rtype == CHAR)
  856.                 {
  857.                     Truncount++;
  858.                     count--;    /* read until delim */
  859.                 }
  860.                 else
  861.                 {
  862.                     return (-1);
  863.                 }
  864.             }
  865.         }
  866.         if (esc)
  867.         {
  868.             esc = FALSE;
  869.             continue;
  870.         }
  871.         if (i == ESCAPE)
  872.         {
  873.             esc = TRUE;
  874.             /*
  875.             ** If esc was stored, back it up.
  876.             */
  877.             if (count >= 0)
  878.             {
  879.                 inp--;        /* remove escape char */
  880.                 count++;    /* restore counter */
  881.             }
  882.         }
  883.         else
  884.         {
  885.             for (dl = mp->fdelim; *dl; dl++)
  886.                 if (*dl == i)
  887.                 {
  888.                     *(inp-1) = '\0';
  889.                     return (1);
  890.                 }
  891.         }
  892.     }
  893. }
  894. /*
  895. **    Look for the existence of a param of the
  896. **    form "0nl" or "00comma" etc.
  897. **
  898. **    Returns the correct delim list or 0
  899. **    if there was a user error
  900. **
  901. **    If successful, a null is inserted at the
  902. **    rightmost '0' so the subsequent atoi will work.
  903. */
  904.  
  905. char *
  906. zcheck(param)
  907. char    *param;
  908. {
  909.     register char    *np, *ret;
  910.  
  911.     np = param;
  912.     ret = Delimitor;    /* assume default delimitors */
  913.  
  914.     if (*np++ == '0')
  915.     {
  916.         /* we have a starting zero. trim the rest */
  917.         while (*np == '0')
  918.             np++;
  919.  
  920.         if (*np > '9' || (*np < '0' && *np >= ' '))
  921.         {
  922.             /* we have a special delim on a 0 width field */
  923.             if (ret = dumvalue(np))
  924.                 *(--np) = '\0';    /*
  925.                         ** end string before delim
  926.                         ** Do not alter delimitor but
  927.                         ** instead destroy last '0'.
  928.                         */
  929.         }
  930.     }
  931.     return (ret);
  932. }
  933. /*
  934. **    Search list of valid dummy names looking
  935. **    for 'name'. If 'name' is a single char
  936. **    then use just that name else it is
  937. **    an error if the name is not found
  938. */
  939.  
  940. char *
  941. dumvalue(name)
  942. char    *name;
  943. {
  944.     register char    **dp, *np, *ret;
  945.  
  946.     dp = Cpdomains;    /* get list of valid dummy names */
  947.     np = name;
  948.     ret = 0;
  949.  
  950.     /* first look for a matching key word */
  951.     while (*dp)
  952.     {
  953.         if (sequal(np, *dp++))
  954.         {
  955.             ret = *dp;
  956.             break;
  957.         }
  958.         dp++;
  959.     }
  960.  
  961.     /* If single char, use that char */
  962.     if (length(np) == 1)
  963.         ret = np;    /* use first char of name */
  964.     if (ret == 0)
  965.         error(UNRECDUMMY, np, 0);
  966.  
  967.     return (ret);
  968. }
  969.